/**
 * Copyright 2011, Felix Palmer
 * <p>
 * Licensed under the MIT license:
 * http://creativecommons.org/licenses/MIT/
 */

package com.pheelicks.visualizer;

import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.render.BlendMode;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Texture;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.media.audio.AudioRenderer;
import ohos.media.audio.AudioWaver;
import ohos.media.image.PixelMap;
import ohos.media.image.common.AlphaType;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.ScaleMode;
import ohos.media.image.common.Size;

import com.pheelicks.visualizer.renderer.Renderer;

import java.util.HashSet;
import java.util.Set;

public class VisualizerView extends Component implements Component.DrawTask {
    private static final String TAG = "VisualizerView";

    private byte[] mBytes;
    private byte[] mFFTBytes;
    private Rect mRect = new Rect();
    private AudioWaver mVisualizer;

    private Set<Renderer> mRenderers;

    private Paint mFlashPaint = new Paint();
    private Paint mFadePaint = new Paint();

    public VisualizerView(Context context) {
        this(context, null, 0);
    }

    public VisualizerView(Context context, AttrSet attrSet) {
        this(context, attrSet, 0);
    }

    public VisualizerView(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init();
    }

    private void init() {
        mBytes = null;
        mFFTBytes = null;

        mFlashPaint.setColor(new Color(Color.argb(122, 255, 255, 255)));
        // Adjust alpha to change how quickly the image fades
        mFadePaint.setColor(new Color(Color.argb(238, 255, 255, 255)));
        mFadePaint.setBlendMode(BlendMode.SRC_IN);

        mRenderers = new HashSet<Renderer>();
        addDrawTask(this);
        setEstimateSizeListener(new EstimateSizeListener() {
            @Override
            public boolean onEstimateSize(int i, int i1) {
                int width = EstimateSpec.getSize(i);
                int height = EstimateSpec.getSize(i1);
                setEstimatedSize(
                    EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
                    EstimateSpec.getChildSizeWithMode(height, height, EstimateSpec.NOT_EXCEED));
                return true;
            }
        });
    }

    /**
     * Links the visualizer to a player
     *
     * @param player - MediaPlayer instance to link to
     * @param context context
     */
    public void link(AudioRenderer player, Context context) {
        if (player == null) {
            throw new NullPointerException("Cannot link to null MediaPlayer");
        }
        int sessionId = player.getRendererSessionId();
        String bundleName = context.getBundleName();

        // Create the Visualizer object and attach it to our media player.
//        if (mVisualizer != null) {
//            mVisualizer.release();
//            mVisualizer = null;
//        }
        mVisualizer = new AudioWaver(sessionId, bundleName);
        mVisualizer.setDataSize(AudioWaver.getMaxDataSize());

        // Pass through Visualizer data to VisualizerView
        mVisualizer.setWaveDataObserver(new AudioWaver.WaveDataObserver() {
            @Override
            public void onWaveData(byte[] bytes, int i) {
                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        updateVisualizer(bytes);
                    }
                });
            }
        }, AudioWaver.getMinInterval() * 3);

        mVisualizer.setFrequencyDataObserver(new AudioWaver.FrequencyDataObserver() {
            @Override
            public void onFrequencyData(byte[] bytes, int i) {
                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        updateVisualizerFFT(bytes);
                    }
                });
            }
        }, AudioWaver.getMinInterval() * 3);

        // Enabled Visualizer and disable when we're done with the stream
        mVisualizer.setActivated(true);
    }

    public void addRenderer(Renderer renderer) {
        if (renderer != null) {
            mRenderers.add(renderer);
        }
    }

    public void clearRenderers() {
        mRenderers.clear();
    }

    /**
     * Call to release the resources used by VisualizerView. Like with the
     * MediaPlayer it is good practice to call this method
     */
    public void release() {
        if (mVisualizer != null) {
            mVisualizer.release();
        }
    }

    /**
     * Pass data to the visualizer. Typically this will be obtained from the
     * Visualizer.OnDataCaptureListener call back. See
     *
     * @param bytes
     */
    public void updateVisualizer(byte[] bytes) {
        if (bytes != null) {
            mBytes = bytes.clone();
        }
        invalidate();
    }

    /**
     * Pass FFT data to the visualizer. Typically this will be obtained from the
     * Visualizer.OnDataCaptureListener call back. See
     *
     * @param bytes
     */
    public void updateVisualizerFFT(byte[] bytes) {
        if (bytes != null) {
            mFFTBytes = bytes.clone();
        }
        invalidate();
    }

    boolean mFlash = false;

    /**
     * Call this to make the visualizer flash. Useful for flashing at the start
     * of a song/loop etc...
     */
    public void flash() {
        mFlash = true;
        invalidate();
    }

    PixelMap mCanvasBitmap;
    Canvas mCanvas;

    @Override
    public void onDraw(Component component, Canvas canvas) {
        // Create canvas once we're ready to draw
        mRect.set(0, 0, getWidth(), getHeight());

        if (mCanvasBitmap == null) {
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.size = new Size(getWidth(), getHeight());
            options.pixelFormat = PixelFormat.ARGB_8888;
            mCanvasBitmap = PixelMap.create(options);
        }
        if (mCanvas == null) {
            mCanvas = new Canvas(new Texture(mCanvasBitmap));
        }

        if (mBytes != null) {
            // Render all audio renderers
            AudioData audioData = new AudioData(mBytes);
            for (Renderer r : mRenderers) {
                r.render(mCanvas, audioData, mRect);
            }
        }

        if (mFFTBytes != null) {
            // Render all FFT renderers
            FFTData fftData = new FFTData(mFFTBytes);
            for (Renderer r : mRenderers) {
                r.render(mCanvas, fftData, mRect);
            }
        }

        // Fade out old contents
        mCanvas.drawColor(Color.argb(238, 255, 255, 255), Canvas.PorterDuffMode.MULTIPLY);

        if (mFlash) {
            mFlash = false;
            mCanvas.drawPaint(mFlashPaint);
        }

        canvas.drawPixelMapHolderRect(new PixelMapHolder(mCanvasBitmap),
            new RectFloat(0, 0, getWidth(), getHeight()), mFadePaint);
    }
}